package com.bajiao.third.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.bajiao.common.constant.CommonConstant;
import com.bajiao.common.constant._Whole;
import com.bajiao.common.exception.DataHandleException;
import com.bajiao.common.menu.ResultEnum;
import com.bajiao.common.util.DateUtils;
import com.bajiao.common.util.IPUtils;
import com.bajiao.common.util.RedisUtil;
import com.bajiao.common.util.vans.RegexTool;
import com.bajiao.service.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


/**
 * @Description: 阿里云短信工具类
 * @Author: FanJq
 * @Date: 2023-10-10
 * @Version: V1.0
 */

@Slf4j
@Component
public class AliSmsUtils {

    //短信验证码同一IP发送次数
    public static final String REDIS_KEY_VERIFY_IP = "app_sms_verify:ip:";
    //短信验证码发送次数
    public static final String REDIS_KEY_VERIFY_SEND_NUM = "app_sms_verify:sendNum:";

    private static ConfigService configService;
    @Resource
    public void setConfigService(ConfigService configService) {
        AliSmsUtils.configService = configService;
    }
    private static RedisUtil redisUtil;
    @Resource
    public void setRedisUtil(RedisUtil redisUtil) {
        AliSmsUtils.redisUtil = redisUtil;
    }

    /**
     * @param mobile
     * @param event
     * @param code
     * @param templateCode
     * @description: 阿里短信发送
     * @author: FanJq
     * @date: 2023-10-10
     * @return:
     */
    public static CommonResponse aliSend(String mobile, String event, int code, String templateCode) {
        log.debug("请求短信serve" + DateUtils.now());
        CommonResponse response = new CommonResponse();
        Map<String, Integer> map = new HashMap<>();
        if (StringUtils.isNotBlank(event)) {
            map.put("code", code);
            log.debug("调用redis,Set值" + mobile + event + "code: " + code + "  时间" + System.currentTimeMillis());
            redisUtil.set(mobile + ":" + event, String.valueOf(code), 60 * 5);
            //发起短信验证
            response = sendSms(mobile, JSONObject.toJSONString(map), templateCode);
        }
        return response;
    }

    /**
     * @Description
     * 校验验证码
     * @date 2024/12/25 17:12
     * @author FanJq
     * @param mobile
     * @param event
     * @param captcha
     * @return {@link int}
    */
    public static Boolean check(String mobile, String event, String captcha) {
        if (!RegexTool.mobileCheck(mobile))
            throw new DataHandleException("手机号格式错误");

        //检测redis是否过期
        if (!redisUtil.hasKey(mobile + ":" + event))
            throw new DataHandleException("验证码错误");

        String result = (String) redisUtil.get(mobile + ":" + event);
        //取redis中缓存数据
        if (!StrUtil.equals(result, captcha) || StrUtil.isBlank(result))
            throw new DataHandleException("验证码错误");

        //验证成功,删除redis缓存数据
        redisUtil.del(mobile + ":" + event);
        return Boolean.TRUE;
    }

    /**
     * @param mobile
     * @param jsonParam
     * @param templateCode
     * @description: 发送短信方法(基础方法)
     * @author: FanJq
     * @date: 2023-10-10/6/3
     * @return:
     */
    public static CommonResponse sendSms(String mobile, String jsonParam, String templateCode) {
        CommonResponse response = null;
        try {
            // 可自助调整超时时间
            System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
            System.setProperty("sun.net.client.defaultReadTimeout", "10000");
            //获取配置信息
            Map map = configService.queryNameAndValueByGroup(CommonConstant.ALISMS_CONFIG_GROUP);

            String accessKeyId = (String) map.get("accessKeyId");
            String accessKeySecret = (String) map.get("accessKeySecret");
            String product = (String) map.get("product");
            String domain = (String) map.get("domain");
            String signName = (String) map.get("signName");

            // 初始化acsClient,暂不支持region化
            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
            IAcsClient acsClient = new DefaultAcsClient(profile);

            CommonRequest request = new CommonRequest();
            request.setSysMethod(MethodType.POST);
            request.setSysDomain(domain);
            request.setSysVersion("2017-05-25");
            request.setSysAction("SendSms");

            request.putQueryParameter("RegionId", "cn-hangzhou");
            // 必填:待发送手机号
            request.putQueryParameter("PhoneNumbers", mobile);
            // 必填:短信签名-可在短信控制台中找到
            request.putQueryParameter("SignName", signName);
            // 必填:短信模板-可在短信控制台中找到
            request.putQueryParameter("TemplateCode", templateCode);
            // 可选:模板中的变量替换JSON串,如模板内容为"尊敬的用户,您的验证码为${code}"时,此处的值为
            request.putQueryParameter("TemplateParam", jsonParam);
            // hint 此处可能会抛出异常，注意catch
            response = acsClient.getCommonResponse(request);
        } catch (ClientException e) {
            e.printStackTrace();
        }

        return response;
    }


    /**
     * @Description
     * 发送短信验证码
     * @date 2024/12/25 15:33
     * @author FanJq
     * @param event
     * @param mobile
     * @param code
     * @param request
     * @return {@link Boolean}
    */
    public static Boolean sendCodeMessage(String event, String mobile, Integer code, HttpServletRequest request) {
        //@create by FanJq 2024/12/25 15:41 获取配置信息
        Map map = configService.queryNameAndValueByGroup(CommonConstant.ALISMS_TEMPLATE_CODE);
        String templateCode = map.get(event).toString();
        if (StrUtil.isBlank(templateCode))
            throw new DataHandleException("发送失败！请检查短信配置");
        //@create by FanJq 2024/12/25 16:43 短信缓存数据
        handleCache(request, mobile);
        //发送短信
        CommonResponse sendSmsResponse = aliSend(mobile, event, code, templateCode);
        JSONObject object = JSON.parseObject(sendSmsResponse.getData());
        String resultCode = object.getString("Code");
        log.info("短信返回请求时间" + DateUtils.now() + "返回code" + resultCode);
        if (resultCode != null && resultCode.equals("OK")) {
            return true;
        }
        return false;
    }

    /**
     * @Description
     * 开发环境发送短信验证码
     * @date 2024/12/25 16:45
     * @author FanJq
     * @param event
     * @param mobile
     * @param code
     * @param request
     * @return {@link Boolean}
    */
    public static Boolean devSendCodeMessage(String event, String mobile, Integer code, HttpServletRequest request) {
        //@create by FanJq 2024/12/25 16:43 短信缓存数据
        handleCache(request, mobile);
        redisUtil.set(mobile + ":" + event, String.valueOf(code), 60 * 5);
        return true;
    }

    /**
     * @Description
     * 短信缓存数据
     * @date 2024/12/25 16:40
     * @author FanJq
     * @param request
     * @param mobile
     * @return
    */
    public static void handleCache(HttpServletRequest request, String mobile) {
        //@create by FanJq 2024/12/25 15:41 限制一个手机号一分钟只能发送1次
        Object sendNum = redisUtil.get(REDIS_KEY_VERIFY_SEND_NUM + mobile);
        Integer sendNumInt = Integer.parseInt(Objects.isNull(sendNum) ? "0" : sendNum.toString());
        if (sendNumInt >= _Whole.ONE) {
            log.info("手机号发送频繁{}", mobile);
            throw new DataHandleException("手机号发送频繁，请稍后再试");
        }
        ++sendNumInt;
        redisUtil.set(REDIS_KEY_VERIFY_SEND_NUM + mobile, sendNumInt, 60);

        //一个IP一分钟5次
        String ip = IPUtils.getIpAddr(request);
        Object ipNumStr = redisUtil.get(REDIS_KEY_VERIFY_IP + ip);
        Integer ipNumInt = Integer.parseInt(Objects.isNull(ipNumStr) ? "0" : ipNumStr.toString());
        if (ipNumInt >= _Whole.FIVE) {
            log.info("同一IP发送频繁{}", ip);
            throw new DataHandleException("手机号发送频繁，请稍后再试");
        }
        ++ipNumInt;
        redisUtil.set(REDIS_KEY_VERIFY_IP + ip, ipNumInt, 60);

        //限制一个手机号一天只能发送10次
        Object verifySendNum = redisUtil.get(REDIS_KEY_VERIFY_SEND_NUM + mobile + DateUtils.now().substring(_Whole.ZERO, _Whole.TEN));
        Integer errorNum = Integer.parseInt(Objects.isNull(verifySendNum) ? "0" : verifySendNum.toString());
        if (errorNum >= _Whole.TEN) {
            log.info("同一手机号超过一天10次发送频繁!手机号：{} ", mobile);
            throw new DataHandleException("手机号发送频繁，请明日再试");
        }
        ++errorNum;
        redisUtil.set(REDIS_KEY_VERIFY_SEND_NUM + mobile + DateUtils.now().substring(0, 10), errorNum, DateUtils.getTodaySurplusSeconds());
    }
}
